// Copyright (c) 2015 JustLurking
// 
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

(function () {

// Check we are on the correct page //////////////////////////////////////
var url = location;
if (url.host.toLowerCase () != 'inkbunny.net'
    || url.pathname != '/privatemessageview.php')
	return;

// Some Values ///////////////////////////////////////////////////////////
var xpr = XPathResult,
    doc = document,
   
    prefix1 = '//*[starts-with(@id, "irt_message_")]',
    prefix2 = './td[3]/div',
   
    ctime = (new Date ()).toLocaleString (),
    titleNode = node (prefix1+'/td[3]/div[2]//div/strong', doc),
    title = titleNode ? titleNode.innerText : "Untitled",
    originalURL = 'https://'+ url.host + url.pathname + url.search,

    outputDocument,

    messages,
    i,
    message,
    newTitle,
    messageBody,
    messageTime,
    authorName,
    authorAccount;

// Utility Functions /////////////////////////////////////////////////////

// Simplified XPath evaluate function (assumes you are searching
// the _page_ doc, do not set root to a Node, belonging to a different
// doc).
function xpath (expr, type, root) {
	return doc.evaluate (expr, root, doc.createNSResolver (doc), type);
}

// Get a trimmed string result from an XPath expression
function string (expr, root) {
	return xpath (expr, xpr.STRING_TYPE, root).stringValue.trim ();
}

// Get a node snapshot from an XPath expression
function nodes (expr, root) {
	return xpath (expr, xpr.UNORDERED_NODE_SNAPSHOT_TYPE, root);
}

// Get the first node that matches from an XPath expression
function node (expr, root) {
	return xpath (expr, xpr.FIRST_ORDERED_NODE_TYPE, root).singleNodeValue;
}

// Encode the mandatory entities (since we're using UTF-8 everything else
// can be left as-is).
function encodeEntities (str) {
	return str.replace (/&/g, '&amp;').replace (/</g, '&lt;').replace (/>/g, '&gt;').replace (/"/g, '&quot;').replace (/'/g, '&apos;');
}

// Convert Elements to their string representation, doing things this way
// always yields well-formed XML (though not necessarily valid XHTML).
function elementToString (elm) {
	var localName = elm.localName;
	var attrs = elm.attributes;
	var child = elm.firstNode;

	var ret = '<'+ localName;
	for (var i = 0; i < attrs.length; i++)
		ret +=	' '+
			attrs [i].name+
			'="'+
			encodeEntities (attrs [i].value)+
			'"';
	ret += '>';

	for (var child = elm.firstChild; child; child = child.nextSibling) {
		var type = child.nodeType;
		if (type == 1) {
			ret += elementToString (child);
		} else if (type == 4) {
			ret += '<![CDATA['+ child.nodeValue +']]>';
		} else if (type == 3) {
			ret += encodeEntities (child.nodeValue);
		}
	}

	ret += '</'+ localName +'>';

	return ret;
}

// The Program ///////////////////////////////////////////////////////////

// output head
outputDocument = 
	'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"'+
	' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'+
	'<html xmlns="http://www.w3.org/1999/xhtml">'+
	'<head>'+
	'<title>'+ title +'</title>'+
	'<style type="text/css">'+
	'html{'+
		'margin:0;'+
		'padding:0;'+
		'font-family:sans-serif;'+
		'background:black'+
	'}'+
	'body{'+
		'padding:6pt;'+
		'background:#ddd;'+
		'color:#222;'+
		'max-width:40em;'+
		'margin:0 auto 0 auto'+
	'}'+
	'.preamble{'+
		'font-size:x-small'+
	'}'+
	'.message{'+
		'margin:6pt'+
	'}'+
	'h1{'+
		'text-align:center'+
	'}'+
	'h2{'+
		'font-size:small'+
	'}'+
	'</style>'+
	'</head>'+
	'<body>'+
	'<div class="preamble">'+
	'<div><span class="label">Source:</span> '+
		'<a href="'+ originalURL +'">'+ originalURL +'</a></div>'+
	'<div><span class="label">Created:</span> '+
		'<span id="ctime">'+ ctime +'</span></div>'+
	'</div>';

// Get all Messages
messages = nodes (prefix1, doc);
for (i = 0; i < messages.snapshotLength; ++i) {
	message = messages.snapshotItem (i);

	// Has the thread title changed?
	if (newTitle = string (prefix2+'[2]//div/strong', message))
		outputDocument += '<h1>'+ newTitle +'</h1>';

	// Get the Message
	if (!(messageBody = node (prefix2+'[2]//div/span', message)))
		continue;

	messageTime = string (prefix2+'[1]/text()[1]', message) || '';

	// Get the Author
	authorName = string ('.//a[contains (@class, "widget_userNameSmall")]', message) || '';
	authorAccount = string ('.//img[contains (@class, "shadowedimage")]/../@href', message) || '';

	// Output the Message
	outputDocument +=
		'<div class="message">'+
			'<h2><a class="author" href="'+ authorAccount +'">'+
				authorName+
			'</a>, <span class="time">'+
				messageTime+
			'</span>:</h2>'+
			'<div class="body">'+
				elementToString (messageBody)+
			'</div>'+
		'</div>';
}

// Output tail.
outputDocument +=
	'</body>'+
	'</html>';

// Open a window with the results.
window.open (
	'data:application/xhtml+xml;charset=utf-16,\uFEFF'+outputDocument,
	'_blank'
);

}) ();
